package com.yqh.discovery.impl;

import com.yqh.Constant;
import com.yqh.ServiceConfig;
import com.yqh.discovery.AbstractRegistry;
import com.yqh.exceptions.NetworkException;
import com.yqh.utils.NetUtils;
import com.yqh.utils.zookeeper.ZookeeperNode;
import com.yqh.utils.zookeeper.ZookeeperUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooKeeper;

import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 阳秋豪
 * @createTime 2024/3/1
 */
@Slf4j
public class ZookeeperRegistry extends AbstractRegistry {
    /*-----------------------成员变量-----------------------*/
    // 维护一个 zookeeper 实例
    private final ZooKeeper zookeeper;

    /*-----------------------成员方法-----------------------*/

    /**
     * 注册服务到注册中心
     *
     * @param service 服务的配置内容
     */
    @Override
    public void register(ServiceConfig<?> service) {
        // 服务的节点路径
        String parentNodePath = Constant.BASE_PROVIDERS_PATH + "/" + service.getInterfaceProvider().getName();
        // 如果服务节点不存在，则创建新的服务节点（持久节点）
        if (!ZookeeperUtils.exists(zookeeper, parentNodePath, null)) {
            ZookeeperNode zookeeperNode = new ZookeeperNode(parentNodePath, null);
            ZookeeperUtils.createNode(zookeeper, zookeeperNode, null, CreateMode.PERSISTENT);
        }
        // 创建本机的临时节点路径 -- 父节点路径 + ip:port
        // 服务提供方的接口一般自己设定，我们还需要一个获取 ip 的方法
        // ip 我们通常是需要一个局域网 ip，不是 127.0.0.1，也不是 ipv6
        // TODO -- 后续处理端口的问题
        String nodePath = parentNodePath + "/" + NetUtils.getIp() + ":" + 8088;
        if (!ZookeeperUtils.exists(zookeeper, nodePath, null)) {
            ZookeeperNode zookeeperNode = new ZookeeperNode(nodePath, null);
            ZookeeperUtils.createNode(zookeeper, zookeeperNode, null, CreateMode.EPHEMERAL);
        }
        if (log.isDebugEnabled()) {
            log.debug("{}服务已将被注册", service.getInterfaceProvider().getName());
        }
    }

    @Override
    public InetSocketAddress lookup(String serviceName) {
        // 1、找到服务对应的节点
        String serviceNodePath = Constant.BASE_PROVIDERS_PATH + "/" + serviceName;
        // 2、从 zookeeper 中获取它的子节点 192.168.250.247:8088
        List<String> children = ZookeeperUtils.getChildren(zookeeper, serviceNodePath, null);
        // 获取了所有的可用服务列表
        List<InetSocketAddress> inetSocketAddresses = children.stream().map(ipString -> {
            String[] ipAndPort = ipString.split(":");
            String ip = ipAndPort[0];
            int port = Integer.parseInt(ipAndPort[1]);
            return new InetSocketAddress(ip, port);
        }).toList();
        if (inetSocketAddresses.isEmpty()) {
            throw new NetworkException("没有发现任何可用的服务主机");
        }
        // TODO q:我们每次调用相关方法的时候都需要去注册中心拉取服务列表吗？ 本地缓存 + watcher
        //        我们如何合理的选择一个可用的服务，而不是只获取第一个      负载均衡策略
        return inetSocketAddresses.get(0);
    }

    /*-----------------------构造方法-----------------------*/
    public ZookeeperRegistry() {
        this.zookeeper = ZookeeperUtils.createZookeeper();
    }

    public ZookeeperRegistry(String connectString, int timeout) {
        this.zookeeper = ZookeeperUtils.createZookeeper(connectString, timeout);
    }
}
